SessionKey handshake for V1-initial S7-1200 PLCs#775
Open
gijzelaerr wants to merge 8 commits into
Open
Conversation
Rebased onto the s7commplus/ package split (#774). All session_auth code now lives under s7commplus/session_auth/ instead of s7/session_auth/. Adds the full session_auth package for V1-initial S7-1200 PLCs that require a SessionKey handshake before data operations: - s7commplus/session_auth/: Public-key-based session authentication (Family 0 key derivation, fingerprint matching, SecurityKeyEncryptedKey blob generation, legitimation challenge solver, HMAC key derivation) - V3 HMAC framing for post-auth data operations - TIA-style CreateObject attributes (required by V1-initial firmware) - PAOM string stripping in ServerSessionVersion echo - Post-auth legitimation flow (challenge read + solved blob write) - Connection.connect(password=) parameter for auth flow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ation - Add type annotations for hashmod parameters in HKDF helpers - Fix frame_version type annotation (int vs ProtocolVersion) - Remove duplicated _skip_typed_value type-dispatch block (dead code after return) - Guard auth_result unpacking with explicit None check for mypy - Add None guards for session_key and challenge in _post_auth_legitimation - Add assert/guard for Optional connection in substreamed client methods Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Analysis of TIA Portal V19 pcap captures (xBiggs, GH-710) revealed the root cause of the ConnectionReset after SessionKey auth: TIA Portal's post-SetupSession sequence: 1. SET_VARIABLE (0x04F2) — write attribute 323 on session 2. GET_VAR_SUBSTREAMED — read from InObj=50, addr 7920 (finalize) 3. Data reads work immediately (no password needed) 4. Legitimation (addr 303/1846) happens later, optionally Our code was skipping steps 1-2 and jumping straight to legitimation (read challenge → write blob → finalize), which caused the PLC to return V254 SYSTEM_EVENT error 0xE9 and RST subsequent requests. Changes: - Add _session_activate() that mirrors TIA Portal's activation: SET_VARIABLE attr 323 + GET_VAR_SUBSTREAMED InObj=50 addr 7920 - Only call _post_auth_legitimation() when a password is provided (previously ran unconditionally, even with empty password) - Reorder: activate first, then legitimation (if password given) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace duplicate _skip_typed_value (73 lines) with codec.skip_typed_value import; fixes DWORD bug (was VLQ instead of fixed 4 bytes) - Initialize _session_auth_public_key/_session_auth_family in __init__ instead of using hasattr() guards - Remove redundant lazy imports (DataType, LegitimationId already at top level) - Make SessionKey and TLS auth mutually exclusive in client.connect() Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
gijzelaerr
force-pushed
the
fix-tls-layering-v2
branch
2 times, most recently
from
July 16, 2026 18:15
3a5660f to
6a3b019
Compare
Move the 95K+ lines of machine-transpiled monolith transforms, constant tables, and binary data into family0/_generated/ to clearly separate opaque generated code from the human-readable orchestration layer. Add ARCHITECTURE.md documenting: - When SessionKey auth is needed (V1-initial vs TLS) - The full authentication flow (diagram) - Module map: which file does what - How the 180-byte SecurityKeyEncryptedKey blob is built - References to upstream HarpoS7, Black Hat papers Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Data2Collection table 1 had 2111 entries (4222 bytes) but its 33 operations require 2112 entries (4224 bytes). The missing final word 0x4C28 caused IndexError during session key renewal when a challenge selected the last lookup block. Backported from HarpoS7 commit 22b9dc0 (bonk-dev/HarpoS7#24). Adds: - The missing 0x4C28 entry in fp_data2.bin - Lookup table size invariant test - Regression test with captured renewal challenge 5B15B469... Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Analysis of all 4 TIA Portal pcaps (GH-710 + GH-728) shows TIA consistently sends 4 requests after SetupSession, not 2: 1. SET_VARIABLE attr 323 = USINT(5) 2. GET_VAR_SUBSTREAMED InObj=50, addr 7920 3. GET_VAR_SUBSTREAMED InObj=session, addr 1842 4. GET_VAR_SUBSTREAMED InObj=50, addr 7920 (again) Previously we only did steps 1-2. Add steps 3-4 and extract a _build_get_var_substreamed helper to reduce boilerplate in both _session_activate and _post_auth_legitimation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move _parse_create_object_response (120 lines, connection.py) into codec.py as parse_create_object_attributes — a reusable function that returns ServerSessionVersion, public key fingerprint, and session challenge in a CreateObjectAttributes dataclass. Both sync connection and async_client can use it. - Keep parse_server_session_version as a thin wrapper for backward compat. - Fix: add session_auth/**/*.bin to pyproject.toml package-data so binary data files (fp_data, transform12 metadata) ship in wheels. Net: -69 lines. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces #761, rebased onto the s7commplus/ package split (#774).
Summary
Adds the full
session_authpackage for V1-initial S7-1200 PLCs that require a SessionKey handshake before data operations:s7commplus/session_auth/: Public-key-based session authentication (Family 0 key derivation, fingerprint matching, SecurityKeyEncryptedKey blob generation, legitimation challenge solver, HMAC key derivation)Connection.connect(password=)parameter for auth flowTest plan
Known limitations
01:prefix) public keys are includedsend_requestneeds V3 HMAC framing wired in for data operations🤖 Generated with Claude Code